﻿using System;
using Framework.Core;
using Framework.Core.BootStrapper.WindowsService;
using Framework.Core.CommandLine;
using Xunit;

namespace Framework.Tests.Core.CommandLine
{
    // ReSharper disable ClassNeverInstantiated.Global
    // ReSharper disable InconsistentNaming
    // ReSharper disable UnusedMember.Global
    // ReSharper disable UnusedAutoPropertyAccessor.Global
    public class CommandLineTest
    {
        [CommandLineArgs(
            Name = "isTrue",
            ShortName = "it",
            ArgumentRequirements = ArgumentRequirements.Required
            )]
        public bool isTrue { get; set; }

        [CommandLineArgs(
            Name = "isTrue",
            ShortName = "it",
            ArgumentRequirements = ArgumentRequirements.Required
            )]
        public string True { get; set; }
    }

    public class CommandLineRequiredOptional
    {
        [CommandLineArgs(
            ArgumentRequirements = ArgumentRequirements.Required | ArgumentRequirements.Optional
            )]
        public string Test { get; set; }
    }

    public class CommandLineRequiredEmpty
    {
        [CommandLineArgs(
            ArgumentRequirements = ArgumentRequirements.Required
            )]
        public string Toto { get; set; }
    }

    public class CommandLineRequiredDefaultValue
    {
        [CommandLineArgs(
            ArgumentRequirements = ArgumentRequirements.Required,
            DefaultValue = "toto"
            )]
        public bool isTrue { get; set; }
    }

    public class CommandLineOptional
    {
        [CommandLineArgs(
            ArgumentRequirements = ArgumentRequirements.Optional,
            DefaultValue = EnvMode.Prod
            )]
        public EnvMode Environment { get; set; }

        [CommandLineArgs(
            ArgumentRequirements = ArgumentRequirements.Optional
            )]
        public bool OptionalPouet { get; set; }
    }

    public enum Mode
    {
        Mode1,
        Mode2
    }

    public class CommandLineBase
    {
        [CommandLineArgs(
            ArgumentRequirements = ArgumentRequirements.Required
            )]
        public Mode Mode { get; set; }

        [CommandLineArgs(
            ArgumentRequirements = ArgumentRequirements.Required
            )]
        public string Name { get; set; }

        [CommandLineArgs(
            ArgumentRequirements = ArgumentRequirements.Required
            )]
        public bool? IsTrue { get; set; }

        [CommandLineArgs(
            ArgumentRequirements = ArgumentRequirements.Required,
            Name = "pouetpouet"
            )]
        public int? Pouet { get; set; }

        [CommandLineArgs(
            ArgumentRequirements = ArgumentRequirements.Optional
            )]
        public int? OptionalPouet { get; set; }

        [CommandLineArgs(
            ArgumentRequirements = ArgumentRequirements.Optional,
            ShortName = "os"
            )]
        public int OptionalShort { get; set; }

        [CommandLineArgs(
            ShortName = "os2"
            )]
        public int Short2 { get; set; }

        [CommandLineArgs(
            ShortName = "C",
            ArgumentRequirements = ArgumentRequirements.Optional
            )]
        public bool IsConsoleMode { get; set; }

        [CommandLineArgs(
            ShortName = "t",
            ArgumentRequirements = ArgumentRequirements.Optional
            )]
        public bool IsTest { get; set; }
    }
    // ReSharper restore InconsistentNaming
    // ReSharper restore ClassNeverInstantiated.Global
    // ReSharper restore UnusedMember.Global
    // ReSharper restore UnusedAutoPropertyAccessor.Global

    public class CommandeLineParserTest
    {
        private readonly ICommandLineParser target = new CommandLineParser();

        [Fact]
        public void Test_ParseEmpty_Succeed()
        {
            var actual = target.Parse<CommandLineOptional>(new string[] { });
            Assert.False(actual.OptionalPouet);
            Assert.Equal(EnvMode.Prod, actual.Environment);
        }

        [Fact]
        public void Test_ParseNoNameNoShortName_Succeed()
        {
            var actual = target.Parse<CommandLineBase>(new[] { "/isTrue:false", "/pouetpouet:10", "/Name:toto", "/os:5", "/os2:2", "/C", "/Mode:Mode1" });
            Assert.Equal(false, actual.IsTrue);
            Assert.Equal(10, actual.Pouet);
            Assert.Equal("toto", actual.Name);
            Assert.Equal(false, actual.OptionalPouet.HasValue);
            Assert.Equal(5, actual.OptionalShort);
            Assert.Equal(2, actual.Short2);
            Assert.Equal(true, actual.IsConsoleMode);
            Assert.Equal(false, actual.IsTest);
            Assert.Equal(Mode.Mode1, actual.Mode);
        }

        [Fact]
        public void Test_parse_no_name_no_short_name_on_same_line_succeed()
        {
            var actual = target.Parse<CommandLineBase>(new[] { "/isTrue:false /pouetpouet:10 /Name:toto /os:5 /os2:2 /C /Mode:Mode1" });
            Assert.Equal(false, actual.IsTrue);
            Assert.Equal(10, actual.Pouet);
            Assert.Equal("toto", actual.Name);
            Assert.Equal(false, actual.OptionalPouet.HasValue);
            Assert.Equal(5, actual.OptionalShort);
            Assert.Equal(2, actual.Short2);
            Assert.Equal(true, actual.IsConsoleMode);
            Assert.Equal(false, actual.IsTest);
            Assert.Equal(Mode.Mode1, actual.Mode);
        }

        [Fact]
        public void Test_Parse_ArgumentNullException_Failed()
        {
            Assert.Throws<ArgumentNullException>(() => target.Parse<CommandLineTest>(null));
        }

        [Fact]
        public void Test_Parse_ArgumentException_Failed()
        {
            Assert.Throws<ArgumentException>(() => target.Parse<CommandLineRequiredOptional>(new[] { "/isTrue:false" }));
            Assert.Throws<ArgumentException>(() => target.Parse<CommandLineRequiredEmpty>(new[] { "/isTrue:   " }));
            Assert.Throws<ArgumentException>(() => target.Parse<CommandLineRequiredDefaultValue>(new[] { "/isTrue:JoeTheArgument" }));
        }

        // ReSharper disable ClassNeverInstantiated.Local
        // ReSharper disable UnusedAutoPropertyAccessor.Local
        private class MultipleNameArgs
        // ReSharper restore ClassNeverInstantiated.Local
        {
            [CommandLineArgs(
                ShortName = "C",
                ArgumentRequirements = ArgumentRequirements.Optional
                )]
            public bool IsConsoleMode { get; set; }

            [CommandLineArgs(
                ShortName = "M",
                ArgumentRequirements = ArgumentRequirements.Optional
                )]
            public string MultipleArgs { get; set; }
        }
        // ReSharper restore UnusedAutoPropertyAccessor.Local

        [Fact]
        public void Should_be_console_mode()
        {
            Assert.True(target.Parse<MultipleNameArgs>(new[] { "/C:true" }).IsConsoleMode);
            Assert.True(target.Parse<MultipleNameArgs>(new[] { "/C" }).IsConsoleMode);
            Assert.True(target.Parse<MultipleNameArgs>(new[] { "/IsConsoleMode:true" }).IsConsoleMode);
            Assert.True(target.Parse<MultipleNameArgs>(new[] { "/IsConsoleMode" }).IsConsoleMode);
        }

        [Fact]
        public void All_args_in_one_line()
        {
            var args = target.Parse<MultipleNameArgs>(new[] { "/C:true /M:toto" });

            Assert.True(args.IsConsoleMode);
            Assert.Equal("toto", args.MultipleArgs);
        }

        [Fact]
        public void Sould_extract_internal_args()
        {
            var args = target.Parse<MultipleNameArgs>(new[] { @"/C:true /M:""/ua:pouet /toto:titi""" });

            Assert.True(args.IsConsoleMode);
            Assert.Equal("/ua:pouet /toto:titi", args.MultipleArgs);
        }

        [Fact]
        public void Sould_escape_if_quote_are_dropped()
        {
            var args = target.Parse<MultipleNameArgs>(new[] { @"/C:true /M:{/ua:pouet /toto:titi /C:true}" });

            Assert.True(args.IsConsoleMode);
            Assert.Equal("/ua:pouet /toto:titi /C:true", args.MultipleArgs);
        }

        [Fact]
        public void Should_test_windows_service_args()
        {
            var args = target.Parse<WindowsServiceArgs>(new[]
                                                 {
                                                     @"/p:WcfHost", 
                                                     "/i", 
                                                     "/n:WcfHost",
                                                     @"/dn:""Windows Service Wcf Hosting""", 
                                                     @"/a:""/p:WcfHost /Env:Prod"""
                                                 });

            Assert.False(args.IsConsoleMode);
            Assert.True(args.IsInstallMode);
            Assert.Equal("WcfHost", args.Name);
            Assert.Equal("Windows Service Wcf Hosting", args.DisplayName);
            Assert.Equal("/p:WcfHost /Env:Prod", args.Args);
        }
    }
}